#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

'''FileHostProtoType contains two class prototype for filehost'''

#import urlparse

import urllib.request
from urllib.parse import urlparse
from http import cookiejar
import sys
import os
import re
import time
import threading
#import StringIO
import gzip
import io
from io import StringIO
import traceback
import types

#3rd-part lib
#import pycurl
#try:
#    import win32api
#except:
#    pass

#intra-modules
from ..poster.encode import multipart_encode
from ..poster.encode import postfile_multipart_encode
from ..poster.streaminghttp import register_openers
from ..TaskManager import UserStop

#def upload_to_ftp_by_curl(url, userpwd, file, target='', target_dir='/', callback=None):
#    '''
#    url : ftp://host:port, without '/', without user/pwd
#    userpwd : usr:pwd style
#    file : file fullpath, unicode
#    target : filename to save in host
#    target_dir : save file to host dir, must end with '/'
#    '''
#    
#    target = not target and os.path.basename(file).encode('utf8') or target
#    url = url + target_dir + target
#    
#    process = pycurl.Curl()  
#    
#    response = StringIO.StringIO()
#    process.setopt(pycurl.WRITEFUNCTION, response.write)    
#    if callback is not None:
#        process.setopt(pycurl.NOPROGRESS, 0)
#        process.setopt(pycurl.PROGRESSFUNCTION, callback)
#        
#    process.setopt(pycurl.URL, url)
#    process.setopt(pycurl.USERPWD, userpwd)
#    process.setopt(pycurl.UPLOAD, 1)
#    process.setopt(pycurl.INFILE, open(file, 'rb'))
#    process.setopt(pycurl.INFILESIZE, os.path.getsize(file))
#    #process.setopt(pycurl.BUFFERSIZE, 1024*64)
#    process.perform()
#    process.close()
#    
#
#def post_by_curl(url, data={}, cookies={}, headers={}, callback=None, rename_func=None, proxy=''):
#    '''
#    
#    curl accept string but not unicode
#    '''
#    process = pycurl.Curl()    
#    
#    process.setopt(pycurl.URL, url)
#    process.setopt(pycurl.FOLLOWLOCATION, 1)
#    response = StringIO.StringIO()
#    process.setopt(pycurl.WRITEFUNCTION, response.write)
#    #process.setopt(pycurl.BUFFERSIZE, 1024*128)
#    if callback is not None:
#        process.setopt(pycurl.NOPROGRESS, 0)
#        process.setopt(pycurl.PROGRESSFUNCTION, callback)
#    
#    if proxy:
#        process.setopt(pycurl.PROXY, proxy.encode('utf-8'))
##        process.setopt(pycurl.PROXYPORT, 8118)
##        process.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
#
#    #header
#    default_headers = {}
#    for keyword in default_headers:
#        if not headers.has_key(keyword):
#            headers[keyword] = default_headers[keyword]        
#    process.setopt(pycurl.HTTPHEADER, \
#                   [('%s: %s' % (a, b)) for a, b in headers.iteritems()])
#    
#    #cookies
#    parts = []
#    if type(cookies) == type({}):
#        for name, value in cookies.iteritems():
#            parts.append('%s=%s;' % (urllib.quote_plus(name),
#                                     urllib.quote_plus(value)))
#            process.setopt(pycurl.COOKIE, ''.join(parts)) 
#    else:
#        #cookiejar
#        print 'use cookiejar'
#        process.setopt(pycurl.COOKIEJAR, cookies)
#        
#    #file-like object param    
#    for key, value in data.iteritems():
#        if isinstance(value, file):
#            
#            safefilename = value.name
#            basename = os.path.basename(value.name)
#            if rename_func is not None:
#                basename = rename_func(value.name)
#            #take care path for windows os
#            if isinstance(safefilename, unicode):
#                try:
#                    safefilename = win32api.GetShortPathName(value.name)
#                except:
#                    pass
#                safefilename = safefilename.encode(sys.getfilesystemencoding())                       
#                basename = basename.encode('utf8')
#            data[key] = (pycurl.FORM_FILE, safefilename,\
#                         pycurl.FORM_FILENAME, basename)
#            
#    process.setopt(pycurl.HTTPPOST, data.items())
#    
#    process.perform()
#    process.close()
#    
#    return response.getvalue()

def postRequest(opener, url, data={}, cb=None, retry=3, setfilename=None, setfilesize=None):
    '''
    post a request to url
    '''
    response = None
    if not data:
        request = urllib.request.Request(url)
    else:
        datagen, headers = postfile_multipart_encode(data, cb=cb, \
                                                     setfilename=setfilename, setfilesize=setfilesize)
        request = urllib.request.Request(url, datagen, headers)
    while(retry > 0):
        try:
            response=opener.open(request)
            break
        except UserStop:
#            traceback.print_exc()
            return None
        except:
            traceback.print_exc()
            print('Fail to Get page data, retry...')
            print(url)
        time.sleep(30)
        retry -= 1
    if response is None:
        return response
    else:
        html = response.read()
        if response.headers.get('Content-Encoding') == 'gzip':
            gzipper = gzip.GzipFile(fileobj=io.BytesIO(html), mode="rb")
            html = gzipper.read()
        html = html.decode('utf-8','ignore')
        return html

def rename_by_time_hash(name):
    filetime = time.gmtime(os.path.getmtime(name))
    filetime = time.strftime('%Y-%m-%d_', filetime)
    prefix = os.path.splitext(name)[0]
    ext = os.path.splitext(name)[1]
    #rar ext
    rar_part_ext = ''
    if ext.lower() == '.rar':
        preprefix = os.path.splitext(prefix)
        if 'part' in preprefix[1].lower():
            rar_part_ext = preprefix[1] 
            prefix = preprefix[0]
    return filetime + str(hash(prefix)) + rar_part_ext + ext

class HostItem(object):
    '''
    an object to record host file infos.
    
    Attributes:
        name: file name
        type: file or folder
        id: internal id of this file in file server
        link: link to download this file    
    '''
    def __init__(self, name, type, id, link, encrypt_name='', server_name=''):
        '''init a item by assign attrs'''
        self.type = type
        self.id = id
        self.link = link
        
        self.name = name
        self.server_name = name
        self.encrypt_name = name  
             
        if server_name:
            self.server_name = server_name
        if encrypt_name:
            encrypt_name = os.path.split(encrypt_name)[1]
            self.encrypt_name = encrypt_name
        
    def format(self, format=''):
        '''output a formated text for pub'''
        outputname = self.name
        if self.encrypt_name:
            if not self.encrypt_name == self.name:
                outputname = self.name + ' = ' + self.encrypt_name
                
        if format == '':
            return "%s - %s" % (outputname, self.link)
                
        if format.lower() == 'bbcode':
            return '[url="%s"]%s[/url]'%(self.link, outputname)
                
        if format.lower() == 'html':
            return '<a href="%s">%s</a>'%(self.link, outputname)
        
        if format:
            format = format.replace('%rename%', self.encrypt_name)
            format = format.replace('%name%', self.name)
            format = format.replace('%link%', self.link)
            return format
    
    def __lt__(self, other):
        attrs = ['name', 'encrypt_name']
        myattrs = [getattr(self, a) for a in attrs]
        oattrs = [getattr(other, a) for a in attrs]
        return (myattrs < oattrs)
    def __eq__(self, other):
        return self.name == other.name and self.encrypt_name == other.encrypt_name
    
        
    def __str__(self):
        '''override __str__ function for print'''
        str = "%s - %s" % (self.name, self.link)
        #__str__ have to return a string, but not unicode
        str = str.encode(sys.getfilesystemencoding(), 'ignore')
        return str
    
class FileHostProcess(object):
    '''
    ProtoType Class of Host
    
    Attributes:
        _user: accout name
        _pwd: accout pwd
        newupload: a list to save the new uploaded file infos
        existedFiles: a list to save the found file infos
        _opener: http opener object with cookie
        _cj: http cookie
        _cookieCurl: filepath of cookie saved by _cj, used by curl
        loginLock: for threading excluse when login
        fhout: output stdout to somewhere 
        fherr: output stderr to somewhere
    '''
    def __init__(self, fhout=sys.stdout, fherr=sys.stderr):
        self.other_params = {}
        self.newupload = []
        self.existedFiles = []
        self._user = ''
        self._pwd = ''
        self._opener = None
        self._cj = None
        self.wait_time = 180
        self.fhout = fhout
        self.fherr = fherr
        self._cookieCurl = ''
        self.rename_func = self.default_rename_func
        self.loginLock = threading.Lock()
        self.proxy = ''
        self.curl_proxy = ''
        self.logtype='curl'
    
    def get_timeticket(self, bits=13):
        timetick = int(time.time()*1000)
        add = '0'*bits
        return (str(timetick)+add)[0:bits]
            
    def add_other_params(self, key, value):
        self.other_params[key] = value
        
    def set_proxy(self, proxy):
        self.proxy = proxy
        
    def set_curl_proxy(self, proxy):
        self.curl_proxy = proxy
    
    def default_rename_func(self, name):
        return os.path.basename(name)
    
    def rename_by_time_hash(self, name):
        return rename_by_time_hash(name)
        
    def set_rename_func(self, rename_func):
        self.rename_func = rename_func
        
    def set_wait_time(self, wtime):
        self.wait_time = wtime
        
    def get_cookies(self):
        '''
        return a dict-format cookies
        '''
        cookies = {}
        if self._cj is None:
            return cookies
        for cookie in self._cj:
            cookies[cookie.name] = cookie.value
        return cookies
    
    def set_upload_mode(self, mode):
        if mode == 'ftp':
            self.upload_web = self.upload_to_ftp
        else:
            self.upload_web = self.upload_web_local
    
    def upload_file_to_ftp(self, url, userpwd, file, target='', target_dir='/', callback=None):
        upload_to_ftp_by_curl(url, userpwd, file, target, target_dir, callback)
            
    def post_file(self, url, data={}, cookies={}, headers={}, callback=None):
        return post_by_curl(url, data, cookies, headers, callback, self.rename_func, self.curl_proxy)  
    
    def post_request(self, url, data={}, cb=None, retry=3, setfilename=None, setfilesize=None):
        return postRequest(self._opener, url, data, cb, retry, setfilename, setfilesize)      
        
    def setFhOut(self, out):
        '''Set where stdout to output'''
        self.fhout = out
        
    def setFhErr(self, err):
        '''Set where stderr to output'''
        self.fherr = err
        
    def login(self, user, pwd):
        '''
        login to the Host, subclass should implement this function to login
        '''
        raise NotImplementedError
    
    def getFileList(self):
        '''
        list all files in Host, subclass should implement this function
        '''
        raise NotImplementedError
    
    def getFolderList(self):
        '''
        list all folders in Host, subclass should implement this function
        '''
        raise NotImplementedError
    
    def find(self, filenames, filelist=None):
        found = []
        if filelist == None:
            return found      
        filelistlower = [fl.name.lower() for fl in filelist]
        todel = []
        for fl in filenames:
            try:
                index = filelistlower.index(os.path.basename(fl).lower())
                found.append(filelist[index])
                todel.append(fl)
            except:
                continue
        for d in todel:
            filenames.remove(d)
        found.sort()
        return found
    
    def findFolder(self, foldername):
        '''
        check if folder has been exsited in Host
        subclass should implement this function
        '''
        raise NotImplementedError
    
    def moveto(self, items, destFolder):
        '''
        move a few file to a destinate folder
        subclass should implement this function
        '''
        raise NotImplementedError
    
    def upload(self, file):
        '''
        upload a file to Host
        subclass should implement this function
        '''
        raise NotImplementedError
    
    def upload_web(self, file):
        '''
        upload a file to Host by simulate web submit
        subclass should implement this function
        '''
        raise NotImplementedError
    
    def loginFileHost(self, loginurl, logindata):
        '''
        loginurl : url of filehost
        logindata: a dict of data to post
        return: opener object which includes cookies
        '''        
        host = urlparse(loginurl).hostname
        params = logindata      
        self.generateOpener()  
        self._opener.addheaders = [
                ("User-agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 QQDownload/1.7"),
                ("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
                ("Accept-Encoding","gzip,deflate"),
                ("Accept-Language","zh-cn,zh;q=0.5"),
                ("Accept-Charset","GB2312,utf-8;q=0.7,*;q=0.7"),
                ("Referer", 'http://%s/' % host),
                ("Host", host)] 
        return self.post_request(loginurl, params)
        
    def generateOpener(self):
        if self._opener is not None:
            return self._opener
        if self._cj is None:
            self._cj = cookiejar.MozillaCookieJar()
        self._opener = register_openers()
        self._opener.add_handler(urllib.request.HTTPCookieProcessor(self._cj))
        if self.proxy:
            proxy_support = urllib.request.ProxyHandler({'http': 'http://%s' % self.proxy})
            self._opener.add_handler(proxy_support)
            self.fherr.write('Set proxy %s' % self.proxy)
        urllib.request.install_opener(self._opener)
        return self._opener
    
    def get_upload_key(self, content, key, index, sed='"'):
        lines = content.split('\n')
        for line in lines:
            if key in line:
                tokens = line.split(sed)
                value = tokens[index]
                return value
        return ''    
        
    def redirectProgressLog(self, popenInstance, progressLog):
        '''
        redirect stderr outputed by curl to progressLog,
        in order to display progress in somewhere 
        '''
        line=''
        if progressLog is not None:
            while popenInstance.poll() is None :
                ch = popenInstance.stderr.read(1)   
                if ch is None or len(ch) == 0:
                    break
                if ch == '\r':
                    tokens = re.split('\s+',line.strip())
                    if re.match('\d+',tokens[0]) is not None:
                        progressLog.write(tokens)
                    if line.strip().startswith('100'):
                        #once curl output 100%, no need to get stderr more
                        break
                    line = ''
                    time.sleep(1)
                line += ch
    
